<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <script src="jquery-3.3.1.min.js"></script>
    <script src="lodash.min.js"></script>
</head>
<body>
    <script>
        // 赋值
        // 基本数据类型
        var a = 1;
        var b = a;
        console.log(b)  // 1

        a = 2;
        console.log(b)  // 1

        b = 3;
        console.log(a) // 2
        
        // 引用数据类型
        let a2 = { a:1, b:'Trist' };
        let b2 = a2;
        console.log(b2) // {a: 1, b: "Trist"}

        b2.a = 2;
        console.log(a2.a) // 2


        // 浅拷贝
        function clone(obj) {
            var cloneObj = {};
            for(var key of Object.keys(obj)) {
                cloneObj[key] = obj[key];
            }
            return cloneObj;
        }
        
        var a3 = {
            a: 1,
            b: 'Trist',
            c: { d: 1 }
        }
        var b3 = clone(a3)
        console.log(b3)  // {a: 1,b: "Trist",c: {d: 1}}

        a3.a = 2;
        console.log(b3.a) // 1

        b3.b = '张三',
        console.log(a3.b) // Trist

        console.log(a3.c) // 1
        console.log(b3.c) // 1
        b3.c.d = 2;
        console.log(a3.c) // 2
        console.log(b3.c) // 2

        // 深拷贝

        function deepClone(obj, cloneObj) {
            var cloneObj = cloneObj || {};
            for(var i in obj) {
                // 通过遍历判断属性是否为引用类型，此处注意null因为历史遗留bug通过typeof输出为object
                if(typeof obj[i] === 'object' && typeof obj[i] !== null) {
                    // 判断引用值是否为数据 obj[i] instanceof Array
                    cloneObj[i] = (obj[i].constructor === Array) ? [] : {};
                    // 进行递归
                    deepClone(obj[i], cloneObj[i]);
                }else {
                    cloneObj[i] = obj[i];
                }
            }
            return cloneObj;
        }

        
        // 定义一个深拷贝函数  接收目标target参数
        function deepClone2(target) {
            // 定义一个变量
            let result;
            // 如果当前需要深拷贝的是一个对象的话
            if (typeof target === 'object') {
            // 如果是一个数组的话
                if (Array.isArray(target)) {
                    result = []; // 将result赋值为一个数组，并且执行遍历
                    for (let i in target) {
                        // 递归克隆数组中的每一项
                        result.push(deepClone2(target[i]))
                    }
                // 判断如果当前的值是null的话；直接赋值为null
                } else if(target===null) {
                    result = null;
                // 判断如果当前的值是一个RegExp对象的话，直接赋值    
                } else if(target.constructor===RegExp){
                    result = target;
                }else {
                // 否则是普通对象，直接for in循环，递归赋值对象的所有值
                    result = {};
                    for (let i in target) {
                        result[i] = deepClone2(target[i]);
                    }
                }
            // 如果不是对象的话，就是基本数据类型，那么直接赋值
            } else {
                result = target;
            }
            // 返回最终结果
            return result;
        }

        var a4 = {
            a: 1,
            b: 'Trist',
            c: { d: 1 },
            size: [1,2,3]
        }

        var b5 = deepClone2(a4, b5);
        console.log(b5)

        console.log(a4.c) // 1
        console.log(b5.c) // 1
        b5.c.d = 2;
        console.log(a4.c) // 1
        console.log(b5.c) // 2
        
        var b4 = deepClone(a4,b4);
        console.log(b4) 

        a4.a = 2;
        console.log(b4.a) // 1

        b4.b = '张三',
        console.log(a4.b) // Trist

        console.log(a4.c) // 1
        console.log(b4.c) // 1
        b4.c.d = 2;
        console.log(a4.c) // 1
        console.log(b4.c) // 2

        console.log(a4.size) // [1, 2, 3]
        console.log(b5.size) // [1, 2, 3]
        b5.size.push(4,5,6)
        console.log(a4.size) // [1, 2, 3]
        console.log(b5.size) // [1, 2, 3, 4, 5, 6]

        // 浅拷贝实现方式
        // 一：原生JS
        // 二：ES6 assign
        var assign1 = Object.assign({}, a3);
        console.log(assign1); // {a: 2, b: "Trist", c: {d: 1}}

        console.log(a3.c) // 2
        console.log(assign1.c) // 2
        assign1.c.d = 3;
        console.log(a3.c) // 3
        console.log(assign1.c) // 3
        // 三： 属性描述符
        function simpleClone(obj) {
            var copy = Object.create(Object.getPrototypeOf(obj));
            Object.getOwnPropertyNames(obj).forEach(key => {
                var desc = Object.getOwnPropertyDescriptor(obj, key);
                Object.defineProperty(copy, key, desc);
            })
            return copy;
        }
        var es5 = simpleClone(a3);
        console.log(es5) // {a: 2, b: "Trist", c: {d: 1}}

        console.log(a3.c) // 3
        console.log(es5.c) // 3
        es5.c.d = 4;
        console.log(es5.c) // 4
        console.log(es5.c) // 4


        // 四： 对于数组对象而言，扩展运算符
        let aa = [1,2,3,[4,5],6]
        let bb = [...aa]
        console.log(bb);

        // 深拷贝实现方式
        // 一：原生JS
        function deepClone3(obj) {
            if(obj === null ) return null;
            if(typeof obj !== 'object') return obj;
            if(obj.constructor === Date) return new Date(obj);
            var newObj = new obj.constructor();
            for(var key in obj) {
                if(obj.hasOwnProperty(key)) {
                    var val = obj[key];
                    newObj[key] = typeof val === 'object' ? arguments.callee(val) : val;
                }
            }
            return newObj;
        }
        var bbb = deepClone3(a4);
        console.log(bbb); // {a: 2, b: "Trist", c: {d: 1}, size: (3) [1, 2, 3]}

        console.log(a4.size) // [1, 2, 3]
        console.log(bbb.size) // [1, 2, 3]
        bbb.size.push(4,5,6)
        console.log(a4.size) // [1, 2, 3]
        console.log(bbb.size) // [1, 2, 3, 4, 5, 6]
        // 二：JSON
        function JSONClone(obj) {
            return JSON.parse(JSON.stringify(obj))
        }
        var json = JSONClone(a4);
        console.log(json) // {a: 2, b: "Trist", c: {d: 1}, size: (3) [1, 2, 3]}

        console.log(a4.size) // [1, 2, 3]
        console.log(json.size) // [1, 2, 3]
        json.size.push(4,5,6,7)
        console.log(a4.size) // [1, 2, 3]
        console.log(json.size) // [1, 2, 3, 4, 5, 6,7]

        // 三：jquery
        var jquery1 = $.extend(true, {}, a4);
        console.log(jquery1); // {a: 2, b: "Trist", c: {d: 1}, size: (3) [1, 2, 3]}

        console.log(a4.size) // [1, 2, 3]
        console.log(jquery1.size) // [1, 2, 3]
        jquery1.size.push(4,5,6,8,9)
        console.log(a4.size) // [1, 2, 3]
        console.log(jquery1.size) // [1, 2, 3, 4, 5, 6, 8, 9]
        // 四：lodash
        var lodash1 = _.cloneDeep(a4);
        console.log(lodash1); // {a: 2, b: "Trist", c: {d: 1}, size: (3) [1, 2, 3]}

        console.log(a4.size) // [1, 2, 3]
        console.log(lodash1.size) // [1, 2, 3]
        lodash1.size.push(4,5,6,1,2)
        console.log(a4.size) // [1, 2, 3]
        console.log(lodash1.size) // [1, 2, 3, 4, 5, 6, 1, 2]

    </script>
</body>
</html>